#include <scheme.h>
#include "config.h"
#include "../../include/path_def.h"

#include "../../common/xobject.h"
#include "crypto_func.h"
#include "sm3.h"

#define f_dentry        f_path.dentry



static spinlock_t g_user_lock;
static spinlock_t g_file_lock;

static LIST_HEAD g_file_list;
static LIST_HEAD g_user_list;

// ----------------------------------------------------------------- 
static void __clear_user_list(LIST_HEAD * user_list);
static void __clear_file_list(LIST_HEAD * file_list);

// -----------------------------------------------------------------
 
void config_module_init(void)
{
	__list_init(&g_file_list);
	__list_init(&g_user_list);
	spin_lock_init(&g_file_lock);
	spin_lock_init(&g_user_lock);
	
	return;
}

void config_module_destroy(void)
{
	__clear_user_list(&g_user_list);
	__clear_file_list(&g_file_list);
 
	return;
}

static void __clear_user_list(LIST_HEAD * user_list)
{
	LIST_HEAD * entry;
	
	while(!__list_is_empty(user_list)){
		USER_ITEM * item;
		
		__list_remove_head(user_list, entry);
		item = OBJECT_PTR(entry, USER_ITEM, head);
		
		xobject_release(item);
	}
	return;
}

USER_ITEM * lookup_user_item(const int uid)
{
	USER_ITEM * item = NULL;
	LIST_HEAD * entry;

	
	spin_lock(&g_user_lock);
	for(entry = g_user_list.next; entry != &g_user_list; entry = entry->next){
		USER_ITEM * t; 
		t = OBJECT_PTR(entry, USER_ITEM, head);
		if(uid == t->user.user_id){
			item = t;
			xobject_addref(item);
			break;
		}
	}
	spin_unlock(&g_user_lock);		
	return item;
}

FILE_ITEM * lookup_file_item(const unsigned char * digest)
{
	FILE_ITEM * item = NULL;
	LIST_HEAD * entry;
	
	spin_lock(&g_file_lock);
	for(entry = g_file_list.next; entry != &g_file_list; entry = entry->next){
		FILE_ITEM * t; 
		t = OBJECT_PTR(entry, FILE_ITEM, head);
		if(0 == memcmp(digest, t->file.object_uuid, 32)){
			item = t;
			xobject_addref(item);
			break;
		}
	}
	spin_unlock(&g_file_lock);		
	return item;
}

static void __clear_file_list(LIST_HEAD * file_list)
{
	LIST_HEAD * entry;
	
	while(!__list_is_empty(file_list)){
		FILE_ITEM * item;
		
		__list_remove_head(file_list, entry);
		item = OBJECT_PTR(entry, FILE_ITEM, head);
		
		xobject_release(item);
	}
	return;	
}

static void __destroyUserItem(USER_ITEM * item)
{
 	kfree(item);
	return;
}
 
static USER_ITEM * createUserItem(void)
{
	USER_ITEM * item = (USER_ITEM *)kmalloc(sizeof(USER_ITEM), GFP_KERNEL);
	
	if(!item){
		return NULL;
	}
	memset(item, 0, sizeof(USER_ITEM));
	item->objHdr.destroyPtr = (DestroyAtomPtr) __destroyUserItem;
 
	xobject_addref(item);
	return item;
}

static void __destroyFileItem(FILE_ITEM * item)
{
 	kfree(item);
	return;
}
 
static FILE_ITEM * createFileItem(void)
{
	FILE_ITEM * item = (FILE_ITEM *)kmalloc(sizeof(FILE_ITEM), GFP_KERNEL);
	
	if(!item){
		return NULL;
	}
	memset(item, 0, sizeof(FILE_ITEM));
	item->objHdr.destroyPtr = (DestroyAtomPtr) __destroyFileItem;
 
	xobject_addref(item);
	return item;
}

int read_userlist(const char * filename)
{
	int retval = -1;
	LIST_HEAD userList;
	LIST_HEAD abrt;
	
	struct file * fp = NULL;
	mm_segment_t oldfs;
	char *user_label = NULL;
	int isOldFsValid = 0;
 
	int i = 0;
	loff_t fsize;
	int number = 0;
	
	// -- printk("<1> read_userlist start\n");
	
	__list_init(&userList);
	__list_init(&abrt);
	
	fp = filp_open(filename, O_RDONLY | O_SYNC, 0); 
	// -- filp_open(filename, O_RDONLY, 0);
	if(IS_ERR(fp)) {
		printk("<1> open policy file 1 %s error!\n", filename);
		fp = NULL;
 
		goto DONE;
	}

	if(!fp) goto DONE;
	
 	// -- printk("<!> open policy file 2 (%s) success!\n", filename);
 
	#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 0, 0) 
		fsize = i_size_read(fp->f_path.dentry->d_inode);
	#else
		fsize = i_size_read(fp->f_dentry->d_inode);
	#endif
	// -- printk("<1> read_userlist 2\n");
	// -- fsize = fp->f_dentry->d_inode->i_size;
	if((fsize % sizeof(USER_LABEL)) == 0){
		number = fsize / sizeof(USER_LABEL);
	}else{
		goto DONE;
	}
    
	printk("<1> read userlist, user num is %d\n", number);

	user_label = kmalloc(sizeof(USER_LABEL), GFP_KERNEL);
	if(!user_label) {	
		printk("<1> alloc mem for read config file err!\n");
		retval = -ENOMEM;	
		goto DONE;
	}

	isOldFsValid = 1;
	oldfs = get_fs();
	set_fs (KERNEL_DS);
	// -- printk("<1> read_userlist 3\n");
	for (i = 0; i < number; i ++){		
		int ret;
		USER_ITEM * item = NULL;

		#if 1
		ret = vfs_read(fp, user_label, sizeof(USER_LABEL), &fp->f_pos);
		if(ret != sizeof(USER_LABEL)) {
			goto DONE;		
		}
		#endif

		item = createUserItem();
		if(!item) goto DONE;

		memcpy(&(item->user), user_label, sizeof(USER_LABEL));

		__list_add_tail(&userList, &item->head);
	}

	retval = 0;
DONE:	
	
	if(isOldFsValid){
		set_fs(oldfs);
	}
 
	if(0 == retval){
		#if 1
			spin_lock(&g_user_lock);
			__list_migrate(&abrt, &g_user_list);
			__list_migrate(&g_user_list, &userList);
			spin_unlock(&g_user_lock);

			__clear_user_list(&abrt);
		
		#else
			spin_lock(&g_user_lock);
			__clear_user_list(&g_user_list);
			while(!__list_is_empty(&userList)){
				LIST_HEAD * entry;
				
				__list_remove_head(&userList, entry);
				__list_add_tail(&g_user_list, entry);
			}
			spin_unlock(&g_user_lock);
		#endif
	}
	
	__clear_user_list(&userList);
	if(fp){
		filp_close(fp, 0);
	}
	if(user_label){
		kfree(user_label);
	}
	return retval; 
}

int read_filelist(const char * filename)
{
	int retval = -1;
	LIST_HEAD fileList;
	LIST_HEAD abrt;
	
	struct file * fp = NULL;
	mm_segment_t oldfs;
	int isOldFsValid = 0;
 
	int i = 0;
	loff_t fsize;
	int number = 0;
	
	
	__list_init(&fileList);
	__list_init(&abrt);
	fp = filp_open(filename, O_RDONLY | O_SYNC, 0); 
	// -- filp_open(filename, O_RDONLY, 0);
	if(IS_ERR(fp)) {
		printk("open policy file 1 %s error!\n", filename);
		fp = NULL;
 
		goto DONE;
	}
	
	if(!fp) goto DONE;
 
	#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 0, 0) 
		fsize = i_size_read(fp->f_path.dentry->d_inode);
	#else
		fsize = i_size_read(fp->f_dentry->d_inode);
	#endif
	// -- fsize = fp->f_dentry->d_inode->i_size;
	if((fsize % sizeof(FILE_LABEL)) == 0){
		number = fsize / sizeof(FILE_LABEL);
	}else{
		goto DONE;
	}
    
	printk("read fileList, file num is %d\n", number);

	  
	isOldFsValid = 1;
	oldfs = get_fs();
	set_fs (KERNEL_DS);
	
	for (i = 0; i < number; i ++){
		FILE_LABEL file_label;
		int ret;
		FILE_ITEM * item;
		printk("in loop");
		ret = vfs_read(fp, &file_label, sizeof(file_label), &fp->f_pos);
		if(ret != sizeof(file_label)) {
			printk("read [%s] error!\n", filename);
			goto DONE;		
		}
		item = createFileItem();
		if(!item) goto DONE;
		item->file = file_label;
		__list_add_tail(&fileList, &item->head);
	}
	__list_init(&abrt);
	
	spin_lock(&g_file_lock);
	__list_migrate(&abrt, &g_file_list);
	__list_migrate(&g_file_list, &fileList);
	spin_unlock(&g_file_lock);
	
	__clear_file_list(&abrt);
	
	retval = 0;
DONE:	
	
	__clear_file_list(&fileList);
	if(isOldFsValid){
		set_fs(oldfs);
	}
	
	if(fp){
		filp_close(fp, 0);
	}
	return retval; 
}


